package com.novunda.platform.common.web.controller;

import com.novunda.platform.common.validator.BeanValidators;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.beans.PropertyEditorSupport;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * 控制器支持类
 *
 * @author linfeng
 * @since 2016/8/30
 */
public class BaseController {

    /**
     * 日志对象
     */
    protected static final Logger LOG = LoggerFactory.getLogger(BaseController.class);

    public static final String CURRENT_PAGE = "NOV_CURRENT_PAGE_PATH";

    /**
     * 验证Bean实例对象
     */
    @Autowired
    protected Validator validator;

    /**
     * 服务端参数有效性验证
     *
     * @param model  验证的实体对象
     * @param object 验证的实体对象
     * @param groups 验证组
     * @return 验证成功：返回true；严重失败：将错误信息添加到 message 中
     */
    @SuppressWarnings("OverloadedMethodsWithSameNumberOfParameters")
    protected boolean beanValidator(Model model, Object object, Class<?>... groups) {
        try {
            BeanValidators.validateWithException(validator, object, groups);
        } catch (ConstraintViolationException ex) {
            List<String> list = BeanValidators.extractPropertyAndMessageAsList(ex, ": ");
            list.add(0, "数据验证失败：");
            addMessage(model, list.toArray(new String[list.size()]));
            return false;
        }
        return true;
    }

    /**
     * 服务端参数有效性验证
     *
     * @param object 验证的实体对象
     * @param groups 验证组
     * @return 验证成功：返回true；严重失败：将错误信息添加到 flash message 中
     */
    @SuppressWarnings("OverloadedMethodsWithSameNumberOfParameters")
    protected boolean beanValidator(RedirectAttributes redirectAttributes, Object object, Class<?>... groups) {
        try {
            BeanValidators.validateWithException(validator, object, groups);
        } catch (ConstraintViolationException ex) {
            List<String> list = BeanValidators.extractPropertyAndMessageAsList(ex, ": ");
            list.add(0, "数据验证失败：");
            addMessage(redirectAttributes, list.toArray(new String[list.size()]));
            return false;
        }
        return true;
    }

    /**
     * 添加Model消息
     *
     * @param model    model
     * @param messages messages
     */
    @SuppressWarnings("OverloadedMethodsWithSameNumberOfParameters")
    protected void addMessage(Model model, String... messages) {
        StringBuilder sb = new StringBuilder();
        for (String message : messages) {
            sb.append(message).append(messages.length > 1 ? "<br/>" : "");
        }
        model.addAttribute("message", sb.toString());
    }


    /**
     * 添加Model消息
     *
     * @param model    model
     * @param messages messages
     */
    @SuppressWarnings("OverloadedMethodsWithSameNumberOfParameters")
    protected void addRisMessage(Model model, String... messages) {
        StringBuilder sb = new StringBuilder();
        for (String message : messages) {
            sb.append(message).append(messages.length > 1 ? "<br/>" : "");
        }
        model.addAttribute("risMessage", sb.toString());
    }

    /**
     * 添加Flash消息
     *
     * @param redirectAttributes redirectAttributes
     * @param messages           messages
     */
    @SuppressWarnings("OverloadedMethodsWithSameNumberOfParameters")
    protected void addMessage(RedirectAttributes redirectAttributes, String... messages) {
        StringBuilder sb = new StringBuilder();
        for (String message : messages) {
            sb.append(message).append(messages.length > 1 ? "<br/>" : "");
        }
        redirectAttributes.addFlashAttribute("message", sb.toString());
    }

    /**
     * 初始化数据绑定
     * 1. 将所有传递进来的String进行HTML编码，防止XSS攻击
     * 2. 将字段中Date类型转换为String类型
     */
    @InitBinder
    protected void initBinder(WebDataBinder binder) {

        binder.setAutoGrowCollectionLimit(2000);//允许绑定集合个数
        binder.registerCustomEditor(Integer.class, null,
                new CustomNumberEditor(Integer.class, null, true));
        binder.registerCustomEditor(Long.class, null,
                new CustomNumberEditor(Long.class, null, true));

        // String类型转换，将所有传递进来的String进行HTML编码，防止XSS攻击
//        binder.registerCustomEditor(String.class, new StringPropertyEditorSupport());
        // Date 类型转换
        binder.registerCustomEditor(Date.class, new DatePropertyEditorSupport());

        //1、日期
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        CustomDateEditor dateEditor = new CustomDateEditor(df, true);
        //表示如果命令对象有Date类型的属性，将使用该属性编辑器进行类型转换
        binder.registerCustomEditor(Date.class, dateEditor);

    }

    /**
     * 获得referer url
     *
     * @param request HttpServletRequest
     * @return refererUrl
     */
    public String getRefererUrl(HttpServletRequest request) {
        String refererUrl = request.getHeader("referer");
        String prefix;
        if (refererUrl.lastIndexOf(':') > 5) {
            prefix = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
        } else {
            prefix = request.getScheme() + "://" + request.getServerName() + request.getContextPath();
        }
        return refererUrl.substring(prefix.length());
    }

    private static class StringPropertyEditorSupport extends PropertyEditorSupport {
        @Override
        public void setAsText(String text) {
            setValue(text == null ? null : StringEscapeUtils.escapeHtml4(text.trim()));
        }

        @Override
        public String getAsText() {
            Object value = getValue();
            return value != null ? value.toString() : "";
        }
    }

    private static class DatePropertyEditorSupport extends PropertyEditorSupport {
        @Override
        public void setAsText(String text) {
            try {
                setValue(DateUtils.parseDate(text));
            } catch (ParseException e) {
                LOG.error(e.getMessage(), e);
            }
        }
    }
}
